import { upperFirst } from 'lodash-es';

export function getBoundingClientRect(element) {
	if (!element || !element.getBoundingClientRect) {
		return 0;
	}
	return element.getBoundingClientRect();
}

function trim(string) {
	return (string || '').replace(/^[\s\uFEFF]+|[\s\uFEFF]+$/g, '');
}

/* istanbul ignore next */
export function hasClass(el, cls) {
	if (!el || !cls) return false;
	if (cls.indexOf(' ') !== -1) throw new Error('className should not contain space.');
	if (el.classList) {
		return el.classList.contains(cls);
	} else {
		return (' ' + el.className + ' ').indexOf(' ' + cls + ' ') > -1;
	}
}

/* istanbul ignore next */
export function addClass(el, cls) {
	if (!el) return;
	let curClass = el.className;
	const classes = (cls || '').split(' ');

	for (let i = 0, j = classes.length; i < j; i++) {
		const clsName = classes[i];
		if (!clsName) continue;

		if (el.classList) {
			el.classList.add(clsName);
		} else if (!hasClass(el, clsName)) {
			curClass += ' ' + clsName;
		}
	}
	if (!el.classList) {
		el.className = curClass;
	}
}

/* istanbul ignore next */
export function removeClass(el, cls) {
	if (!el || !cls) return;
	const classes = cls.split(' ');
	let curClass = ' ' + el.className + ' ';

	for (let i = 0, j = classes.length; i < j; i++) {
		const clsName = classes[i];
		if (!clsName) continue;

		if (el.classList) {
			el.classList.remove(clsName);
		} else if (hasClass(el, clsName)) {
			curClass = curClass.replace(' ' + clsName + ' ', ' ');
		}
	}
	if (!el.classList) {
		el.className = trim(curClass);
	}
}
/**
 * Get the left and top offset of the current element
 * left: the distance between the leftmost element and the left side of the document
 * top: the distance from the top of the element to the top of the document
 * right: the distance from the far right of the element to the right of the document
 * bottom: the distance from the bottom of the element to the bottom of the document
 * rightIncludeBody: the distance between the leftmost element and the right side of the document
 * bottomIncludeBody: the distance from the bottom of the element to the bottom of the document
 *
 * @description:
 */
export function getViewportOffset(element) {
	const doc = document.documentElement;

	const docScrollLeft = doc.scrollLeft;
	const docScrollTop = doc.scrollTop;
	const docClientLeft = doc.clientLeft;
	const docClientTop = doc.clientTop;

	const pageXOffset = window.pageXOffset;
	const pageYOffset = window.pageYOffset;

	const box = getBoundingClientRect(element);

	const { left: retLeft, top: rectTop, width: rectWidth, height: rectHeight } = box;

	const scrollLeft = (pageXOffset || docScrollLeft) - (docClientLeft || 0);
	const scrollTop = (pageYOffset || docScrollTop) - (docClientTop || 0);
	const offsetLeft = retLeft + pageXOffset;
	const offsetTop = rectTop + pageYOffset;

	const left = offsetLeft - scrollLeft;
	const top = offsetTop - scrollTop;

	const clientWidth = window.document.documentElement.clientWidth;
	const clientHeight = window.document.documentElement.clientHeight;
	return {
		left: left,
		top: top,
		right: clientWidth - rectWidth - left,
		bottom: clientHeight - rectHeight - top,
		rightIncludeBody: clientWidth - left,
		bottomIncludeBody: clientHeight - top,
	};
}

export function hackCss(attr, value) {
	const prefix = ['webkit', 'Moz', 'ms', 'OT'];

	const styleObj = {};
	prefix.forEach((item) => {
		styleObj[`${item}${upperFirst(attr)}`] = value;
	});
	return {
		...styleObj,
		[attr]: value,
	};
}

/* istanbul ignore next */
export function on(element, event, handler) {
	if (element && event && handler) {
		element.addEventListener(event, handler, false);
	}
}

/* istanbul ignore next */
export function off(element, event, handler) {
	if (element && event && handler) {
		element.removeEventListener(event, handler, false);
	}
}

/* istanbul ignore next */
export function once(el, event, fn) {
	const listener = function (_this, ...args) {
		if (fn) {
			fn.apply(_this, args);
		}
		off(el, event, listener);
	};
	on(el, event, listener);
}
